/*
 * Solver.h
 *
 *  Created on: 12.10.2010
 *      Author: jilly
 */

#ifndef SOLVER_H_
#define SOLVER_H_

//MPI constants
#define CHECK_MSG_AMOUNT 100
//types of messages
#define MSG_WORK_REQUEST 1000
#define MSG_WORK_SENT    1001
#define MSG_WORK_NOWORK  1002
#define MSG_END_TOKEN    1003
#define MSG_FINISH       1004
#define MSG_SOLUTION	 1005
#define MSG_BUFFER_SIZE	 1007
#define MSG_SOLUTION_BOUNDS 1008

#define TOKEN_COLOR_WHITE 0
#define TOKEN_COLOR_BLACK 1

#define BUFFER_INIT_SIZE 1000

#include "stack"
#include <stdlib.h>
#include "../objects/Node.h"
#include "../objects/Solution.h"
#include "../objects/Board.h"
#include "../logger/Logger.h"

class Solver
{
private:
	Board* board;
	Logger* logger;
	/**
	 * flags for each processor
	 * 	true - if i asked for job and yet haven't received response (for not sending
	 *  request to processor more than once)
	 *  false - otherwise
	 */
	bool* jobResponsesAwaiting;
	//mpi message buffer
	unsigned char* buffer;
	//size of message buffer
	int bufferSize;
	//local counter - who should i ask for job
	int jobRequestToRank;
	//color of processor - for termination
	int myColor;
	//id of processor
	int myRank;
	//number of processors
	int numberOfProcessors;
	//state variables (flags)
	bool idle;
	bool receivedToken;
	bool workDone;
	bool terminationDone; //flag for processor 0
	bool foundBestSolution; //if we reach lowerBound terminate

	Solution* bestSolution;
	/**
	 * number of best moves
	 * used in traversing the space,
	 * initialized to infinity a if some
	 * solution was found represents number
	 * of moves of currently best solution
	 * (at the end of solving represents
	 * number of moves of the best solution
	 * found)
	 */
	unsigned int bestMoves;
	/**
	 * limit in which the solution
	 * is found in the best case
	 */
	unsigned int lowerBound;
	/**
	 * limit in which the solution
	 * is found in the worst case
	 */
	unsigned int upperBound;
	void init();
	void allocBuffer();
	void reallocBuffer();
	void allocJobResponsesAwaiting();
	/** performs move provided by parameter */
	void performMove(Move* move);
	/**
	 * moves piece (queen / bishop) back to original
	 * position and if it conquered piece, places
	 * piece back on this position
	 */
	void performMoveBack(Move* move);
	/**
	 * expands node 'Node' = pushes all possible moves on stack 'stack'
	 */
	template <typename T> void expandNode(Node* node, T &stack);
	/**
	 * pops all remaining nodes from stack and deletes them from memory
	 */
	template <typename T> void clearStack(T &stack);
	/**
	 * returns if we should skip node 'node' or not,
	 * in other words, that we should not expand this node
	 */
	bool skipNode(Node* node);
	/**
	 * generates solution by traversing tree from 'lastNode' to root
	 */
	Solution* createSolution(Node* lastNode);
	/**
	 * checks if there are any messages from other processors
	 * if so, react to them
	 */
	template <typename T> void doStep(T &stack);
	template <typename T>
	void communicate(T &stack);
	void sendSolution(Solution* solution);
	void sendToken();
	void receiveToken();
	void sendWorkRequest();
	void receiveWorkRequest();
	void receiveSolution();
	void sendBufferSizeMessage();
	void sendSolutionBounds();
	template <typename T> void trySendWork(T &stack, int toRank);
	void sendNoWork(int toRank);
	void receiveNoWork();
	void sendFinish();
	void receiveFinish();
	template <typename T> void receiveWork(T &stackRef);
	void nextNeighborForWork();
	template <typename T> void printStack(T &stackRef);
public:
	Solver();
	Solver(Board* board);
	~Solver();
	/**
	 * initializes solving procedure
	 */
	void startSolve();
	/**
	 * prints diagram with sequence
	 * of moves of the best solution
	 * for current problem (configuration)
	 */
	/**
	 * solves the problem
	 */
	void doSolve(Node * root, int whoStarts);
	void printSolution();
	void writeSolution(ofstream& streamRef);

	const int getMyRank()
	{
		return myRank;
	}
	void setMyRank(int myRank)
	{
		this->myRank = myRank;
	}
	const int getNumberOfProcessors()
	{
		return numberOfProcessors;
	}
	void setNumberOfProcessors(int numberOfProcessors)
	{
		this->numberOfProcessors = numberOfProcessors;
	}
	void receiveBufferSizeMessage();
	void receiveSolutionBounds();
	const unsigned int getBestMoves()
	{
		return bestMoves;
	}
	const unsigned int getLowerBound()
	{
		return lowerBound;
	}
	void initLogger(const char* filename);
};

#endif /* SOLVER_H_ */
